package cn.bellychang.bobo.linkedlist;

import cn.bellychang.bobo.arraylist.Iterator;

/**
 * @author ChangLiang
 * @date 2020/9/11
 */
public class TwoWayLoopLinkedList<E> {

    private int size;

    Node sentinel = new Node();

    public TwoWayLoopLinkedList() {
        sentinel.next = sentinel;
        sentinel.prev = sentinel;
    }

    public int getSize() {
        return this.size;
    }

    public void add(int index, E e) {
        checkAddIndex(index);
        Node newNode = new Node();
        newNode.value = e;
        Node prev = sentinel;
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }
        newNode.next = prev.next;
        prev.next.prev = newNode;
        newNode.prev = prev;
        prev.next = newNode;
        size++;
    }

    private void checkAddIndex(int index) {
        if (index < 0 || index > size) {
            throw new RuntimeException("index out of bound");
        }
    }

    public void addLast(E e) {
        add(size, e);
    }

    public void addFirst(E e) {
        add(0, e);
    }

    public E get(int index) {
        checkGetOrRemoveIndex(index);
        Node temp = sentinel.next;
        for (int i = 0; i < index; i++) {
            temp = temp.next;
        }
        return temp.value;
    }

    public E getFirst() {
        return get(0);
    }

    public E getLast() {
        return get(size - 1);
    }

    public E remove(int index) {
        checkGetOrRemoveIndex(index);
        Node curr = sentinel.next;
        for (int i = 0; i < index; i++) {
            curr = curr.next;
        }
        curr.prev.next = curr.next;
        curr.next.prev = curr.prev;
        size--;
        return curr.value;
    }

    public E removeFirst() {
        return remove(0);
    }

    public E removeLast() {
        return remove(size - 1);
    }

    private void checkGetOrRemoveIndex(int index) {
        if (index < 0 || index >= size) {
            throw new RuntimeException("index out of bound");
        }
    }

    public Iterator<E> iterator() {
        return new Iterator<E>() {

            int count = 0;
            Node temp = sentinel.next;

            @Override
            public boolean hasNext() {
                return count < size;
            }

            @Override
            public E next() {
                Node value = temp;
                count++;
                temp = temp.next;
                return value.value;
            }
        };
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        Iterator<E> iterator = this.iterator();
        while (iterator.hasNext()) {
            builder.append(iterator.next());
            if (iterator.hasNext()) {
                builder.append("->");
            }
        }
        return builder.toString();
    }

    class Node {
        Node prev;

        Node next;

        E value;
    }
}
